/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.boot.oss.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.PathUtil;
import com.iwindplus.boot.domain.constant.CommonConstant;
import com.iwindplus.boot.domain.constant.CommonConstant.ExceptionConstant;
import com.iwindplus.boot.domain.enums.BizCodeEnum;
import com.iwindplus.boot.domain.exception.BizException;
import com.iwindplus.boot.domain.vo.UploadVO;
import com.iwindplus.boot.oss.service.FileService;
import com.iwindplus.boot.util.FilesUtil;
import com.iwindplus.boot.util.HttpsUtil;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

/**
 * 文件操作业务层接口实现类.
 *
 * @author zengdegui
 * @since 2019/8/14
 */
@Slf4j
public class FileServiceImpl extends AbstractOssBaseServiceImpl implements FileService {

    @Autowired
    private ResourceLoader resourceLoader;

    @Override
    public Resource getResource(String relativePath) {
        StringBuilder sb = new StringBuilder(CommonConstant.FileConstant.CLASSPATH)
            .append(CommonConstant.SymbolConstant.COLON).append(relativePath);
        Resource resource = this.resourceLoader.getResource(sb.toString());
        if (!resource.exists()) {
            throw new BizException(BizCodeEnum.FILE_NOT_FOUND);
        }
        return resource;
    }

    @Override
    public UploadVO uploadFile(byte[] data, String prefix, String sourceFileName, Boolean renamed, Boolean returnAbsolutePath) {
        this.checkFile(data);
        String fileName = this.getNewFileName(renamed, sourceFileName);
        String relativePath = this.getRelativePath(prefix, fileName);
        return this.getUploadVO(data, relativePath, sourceFileName, returnAbsolutePath);
    }

    @Override
    public UploadVO uploadFile(byte[] data, String relativePath, String sourceFileName, Boolean returnAbsolutePath) {
        this.checkFile(data);
        return this.getUploadVO(data, relativePath, sourceFileName, returnAbsolutePath);
    }

    @Override
    public UploadVO uploadFile(MultipartFile file, String prefix, Boolean renamed, Boolean returnAbsolutePath) {
        byte[] data = FilesUtil.getBytes(file);
        this.checkFile(data);
        String sourceFileName = file.getOriginalFilename();
        String fileName = this.getNewFileName(renamed, sourceFileName);
        String relativePath = this.getRelativePath(prefix, fileName);
        return this.getUploadVO(data, relativePath, sourceFileName, returnAbsolutePath);
    }

    @Override
    public UploadVO uploadFile(MultipartFile file, String relativePath, Boolean returnAbsolutePath) {
        byte[] data = FilesUtil.getBytes(file);
        this.checkFile(data);
        String sourceFileName = file.getOriginalFilename();
        return this.getUploadVO(data, relativePath, sourceFileName, returnAbsolutePath);
    }

    @Override
    public UploadVO uploadFile(File file, String prefix, Boolean renamed, Boolean returnAbsolutePath) {
        byte[] data = FilesUtil.getBytes(file);
        this.checkFile(data);
        String sourceFileName = file.getName();
        String fileName = this.getNewFileName(renamed, sourceFileName);
        String relativePath = this.getRelativePath(prefix, fileName);
        return this.getUploadVO(data, relativePath, sourceFileName, returnAbsolutePath);
    }

    @Override
    public UploadVO uploadFile(File file, String relativePath, Boolean returnAbsolutePath) {
        byte[] data = FilesUtil.getBytes(file);
        this.checkFile(data);
        String sourceFileName = file.getName();
        return this.getUploadVO(data, relativePath, sourceFileName, returnAbsolutePath);
    }

    @Override
    public boolean removeFiles(List<String> relativePaths) {
        String rootPath = this.getRootPath();
        List<Path> pathList = relativePaths.stream()
            .map(relativePath -> Paths.get(rootPath, relativePath))
            .filter(path -> Files.exists(path))
            .collect(Collectors.toList());
        try {
            for (Path path : pathList) {
                Files.delete(path);
            }
            return true;
        } catch (IOException ex) {
            log.error(ExceptionConstant.IO_EXCEPTION, ex);

            throw new BizException(BizCodeEnum.FILE_DELETE_ERROR);
        }
    }

    @Override
    public void downloadFile(HttpServletResponse response, String relativePath, String fileName) {
        String rootPath = this.getRootPath();
        Path absolutePath = Paths.get(rootPath, relativePath);
        try {
            FilesUtil.downloadFile(Files.newInputStream(absolutePath), this.getNewFileName(relativePath, fileName), response);
        } catch (IOException ex) {
            log.error(ExceptionConstant.IO_EXCEPTION, ex);

            throw new BizException(BizCodeEnum.FILE_DOWNLOAD_ERROR);
        }
    }

    @Override
    public void downloadResourceFile(HttpServletResponse response, String relativePath, String fileName) {
        Resource resource = this.getResource(relativePath);
        try {
            FilesUtil.downloadFile(resource.getInputStream(), super.getNewFileName(relativePath, fileName), response);
        } catch (IOException ex) {
            log.error(ExceptionConstant.IO_EXCEPTION, ex);

            throw new BizException(BizCodeEnum.FILE_DOWNLOAD_ERROR);
        }
    }

    private UploadVO getUploadVO(byte[] data, String relativePath, String sourceFileName, Boolean returnAbsolutePath) {
        try {
            String rootPath = this.getRootPath();
            Path absolutePath = Paths.get(rootPath).resolve(relativePath);
            PathUtil.mkParentDirs(absolutePath);
            final File file = absolutePath.toFile();
            FileCopyUtils.copy(data, file);
            final UploadVO resultData = UploadVO.builder()
                .sourceFileName(sourceFileName)
                .fileName(FileUtil.getName(relativePath))
                .fileSize(file.length())
                .relativePath(relativePath)
                .build();
            if (Optional.ofNullable(returnAbsolutePath).orElse(Boolean.TRUE)) {
                resultData.setAccessDomain(HttpsUtil.getRequestUrl());
                resultData.setAbsolutePath(file.getAbsolutePath());
            }
            return resultData;
        } catch (Exception ex) {
            log.error(ExceptionConstant.EXCEPTION, ex);

            throw new BizException(BizCodeEnum.FILE_UPLOAD_ERROR);
        }
    }
}
